
#pragma once

#include <stdarg.h>

#include <map>
#include <string>
#include <vector>

#include "abs-time.h"
#include "native-condition.h"
#include "native-mutex.h"
#include "queue.h"

namespace dulymoo {

class ISocket;
class IFile;
class IMutex;
class IRWLock;
class ICondition;
class IEvent;
class ISemaphore;

class Fiber;
class FiberContext;
class Thread;

class FiberEnv
{
public:
    FiberEnv();
    ~FiberEnv();

public:
    bool Initialize();
    void Finalize();

    bool Start(unsigned int threads=0);
    void Stop();

    bool AddFiber(void (*func)(void*), void* arg, const char* name=NULL);

    void DumpFibers(FILE * /* TODO: flags to include/exclude state, id, cpu, etc */);

public:
    // static methods are only available when invoking from the running fibers

    static ISocket*    NewSocket(...);
    static IFile*      NewFile(...);
    static IMutex*     NewMutex(bool recursive=true);
    static IRWLock*    NewRWLock(...);
    static ICondition* NewCondition(...);
    static IEvent*     NewEvent(...);
    static ISemaphore* NewSemaphore(...);

    static void Sleep(unsigned long milliseconds);
    static void Yield();
    static void Quit();

    // `state' should be a literal string
    static void SetCurrentFiberState(const char* state);

    static bool CreateFiber(void (*func)(void*), void* arg, const char* name=NULL);

public:
    class ThreadContext
    {
        friend class FiberEnv;

    public:
        FiberContext* GetCurrentFiber();
        bool Schedule();
        bool Schedule(void (*func)(ThreadContext*, void*), void*arg);
        void ReadyFiber(FiberContext*);

    private:
        FiberEnv *env_;
        Fiber *schedule_fiber_;
        FiberContext *current_fiber_;
        void (*post_switch_action_)(ThreadContext*, void*);
        void *post_switch_arg_;
    };

    static ThreadContext* GetThreadContext();

private:
    static void ThreadProc(void*);
    void ThreadProc();

    static void PollSleepThreadProc(void*);
    void PollSleepThreadProc();
    void CreatePollSleepThread();

    void WaitAllFibersToQuit();

private:
    static void CompleteSleep(ThreadContext*, void*);
    static void CompleteYield(ThreadContext*, void*);
    static void CompleteQuit(ThreadContext*, void*);

private:
    bool quit_flag_;

    typedef std::map<unsigned long, FiberContext*> fibers_t;
    fibers_t fibers_;
    NativeMutex fibers_lock_;
    NativeCondition fibers_all_quitted_;

    typedef Queue<FiberContext, NativeMutex, NativeCondition> queue_t;
    queue_t ready_queue_;

    typedef std::multimap<ABSTime, FiberContext*> sleeping_fibers_t;
    sleeping_fibers_t sleeping_fibers_;
    NativeMutex sleeping_fibers_lock_;

    std::vector<Thread*> threads_;

    Thread* poll_sleep_thread_;
    NativeMutex poll_sleep_thread_init_lock_;
};

} // namespace dulymoo

